﻿#include "DAAbstractNode.h"
#include "DAAbstractNodeGraphicsItem.h"
#include "DANodeMetaData.h"

#define SANODE_DEBUG_PRINT

class DAAbstractNodePrivate
{
    DA_IMPL_PUBLIC(DAAbstractNode)
public:
    class LinkData
    {
    public:
        LinkData();
        LinkData(std::shared_ptr<DAAbstractNode> n, const QString &p);
        std::weak_ptr<DAAbstractNode> node;
        QString key;
    };

public:
    DAAbstractNodePrivate(DAAbstractNode *p);
    DANodeMetaData _meta;
    DAAbstractNodeGraphicsItem *_item; ///< 记录从节点生成的item
    QMap<QString, LinkData> _toNode; ///< 所有output连接点对应的node,这里的linkData都是其他
    QMap<QString, LinkData> _fromNode; ///< 所有input连接点对应的node
    QMap<QString, DADataPackage> _inputData; ///< 节点数据
};

DAAbstractNodePrivate::LinkData::LinkData() {}

DAAbstractNodePrivate::LinkData::LinkData(std::shared_ptr<DAAbstractNode> n, const QString &p) : node(n), key(p) {}

DAAbstractNodePrivate::DAAbstractNodePrivate(DAAbstractNode *p) : q_ptr(p), _item(nullptr) {}

DAAbstractNode::DAAbstractNode() : d_ptr(new DAAbstractNodePrivate(this)) {}

/**
 * @brief 节点的销毁，节点销毁过程会通知相关联的节点把自己信息解除
 */
DAAbstractNode::~DAAbstractNode()
{
    detachAll();
}

/**
 * @brief 获取节点的名字
 * @return
 */
QString DAAbstractNode::getNodeName() const
{
    return (d_ptr->_meta.getNodeName());
}

void DAAbstractNode::setNodeName(const QString &name)
{
    d_ptr->_meta.setNodeName(name);
}

/**
 * @brief FCAbstractNode::getNodePrototype
 * @return
 */
QString DAAbstractNode::getNodePrototype() const
{
    return (d_ptr->_meta.getNodePrototype());
}

/**
 * @brief FCAbstractNodeGraphicsItem::getIcon
 * @return
 */
QIcon DAAbstractNode::getIcon() const
{
    return (d_ptr->_meta.getIcon());
}

/**
 * @brief 设置图标
 * @param icon
 */
void DAAbstractNode::setIcon(const QIcon &icon)
{
    d_ptr->_meta.setIcon(icon);
}

/**
 * @brief 获取节点元数据
 * @return
 */
const DANodeMetaData &DAAbstractNode::metaData() const
{
    return (d_ptr->_meta);
}

/**
 * @brief 获取节点元数据
 * @return
 */
DANodeMetaData &DAAbstractNode::metaData()
{
    return (d_ptr->_meta);
}

/**
 * @brief 设置元数据
 * @param metadata
 */
void DAAbstractNode::setMetaData(const DANodeMetaData &metadata)
{
    d_ptr->_meta = metadata;
}

/**
 * @brief 返回自身的引用
 * @return
 */
DAAbstractNode::SharedPointer DAAbstractNode::pointer()
{
    return (shared_from_this());
}

/**
 * @brief 建立连接,从out到另外一个item的in
 * @param outpt 输出点
 * @param toItem 输入的item
 * @param topt 输入点
 * @return 成功返回true
 */
bool DAAbstractNode::linkTo(const QString &outpt, DAAbstractNode::SharedPointer toItem, const QString &inpt)
{
    if (!getOutputKeys().contains(outpt)) {
        qDebug() << "invalid link node[" << getNodeName() << "] can not find out key " << outpt;
        return (false);
    }
    if (!(toItem->getInputKeys().contains(inpt))) {
        qDebug() << "invalid link node[" << getNodeName() << "] toItem can not find inpt " << inpt;
        return (false);
    }
    DAAbstractNodePrivate::LinkData ld;

    ld.node = toItem;
    ld.key = inpt;
    d_ptr->_toNode.insert(outpt, ld);
    toItem->d_func()->_fromNode.insert(inpt, DAAbstractNodePrivate::LinkData(pointer(), outpt));
    return (true);
}

/**
 * @brief detachToLink会对_toNode进行删除操作，因此不允许在_toNode迭代环境中调用此函数
 * @param outpt
 * @return
 */
bool DAAbstractNode::detachToLink(const QString &outpt)
{
    DAAbstractNodePrivate::LinkData ld = d_ptr->_toNode.value(outpt);
    SharedPointer toItem = ld.node.lock();

    if (toItem == nullptr) {
        return (d_ptr->_toNode.remove(outpt) > 0);
    }
    toItem->d_func()->_fromNode.remove(ld.key);
    return (d_ptr->_toNode.remove(outpt) > 0);
}

/**
 * @brief 移除所有依赖，一般是节点被删除时会调用此函数
 */
void DAAbstractNode::detachAll()
{
#ifdef SANODE_DEBUG_PRINT
    qDebug() << "---------------------------";
    qDebug() << "-start detachAll"
             << "\n- node name:" << getNodeName() << ",prototype:" << metaData().getNodePrototype() << "\n- toNode infos:";
    for (auto i = d_ptr->_toNode.begin(); i != d_ptr->_toNode.end(); ++i) {
        SharedPointer p = i.value().node.lock();
        if (p) {
            qDebug() << "- outpoint key is:" << i.key() << ",be linked node inpoint is:" << i.value().key
                     << ",be linked node name is:" << p->getNodeName();
        } else {
            qDebug() << "- outpoint key is:" << i.key() << ",be linked node inputkey is:" << i.value().key
                     << ",be linked node pointer is nullptr,maybe deleted";
        }
    }
    for (auto i = d_ptr->_fromNode.begin(); i != d_ptr->_fromNode.end(); ++i) {
        SharedPointer p = i.value().node.lock();
        if (p) {
            qDebug() << "- inpoint key is:" << i.key() << ",linked node outpoint is:" << i.value().key
                     << ",linked node name is:" << p->getNodeName();
        } else {
            qDebug() << "- inpoint key is:" << i.key() << ",linked node outpoint is:" << i.value().key
                     << ",linked node pointer is nullptr,maybe deleted";
        }
    }
    qDebug() << "- 1.start detachToLink";
#endif
    //! 不能直接迭代_toNode过程调用detachToLink，会导致迭代器失效
    //清空关系
    QList<QString> keys = d_ptr->_toNode.keys();
    for (const QString &k : keys) {
        detachToLink(k);
    }
    QList<DAAbstractNodePrivate::LinkData> lds = d_ptr->_fromNode.values();

    for (DAAbstractNodePrivate::LinkData ld : lds) {
        SharedPointer p = ld.node.lock();
        if (p) {
            p->detachToLink(ld.key);
        }
    }
#ifdef SANODE_DEBUG_PRINT
    qDebug() << "---------end-detachAll----------";
#endif
}

/**
 * @brief 获取node对应的item
 * @return
 */
DAAbstractNodeGraphicsItem *DAAbstractNode::graphicsItem()
{
    return (d_ptr->_item);
}

const DAAbstractNodeGraphicsItem *DAAbstractNode::graphicsItem() const
{
    return (d_ptr->_item);
}

/**
 * @brief @sa createGraphicsItem 最后需调用此函数记录生成的item
 * @param item 内部维护着FCAbstractNode的weakpoint
 */
void DAAbstractNode::registGraphicsItem(DAAbstractNodeGraphicsItem *item)
{
    d_ptr->_item = item;
}

/**
 * @brief 调用FCAbstractNodeGraphicsItem的nodeAction函数，把节点的动作传递到item中
 * @param item
 * @param act
 * @param v
 */
void DAAbstractNode::callItemNodeHasAction(DAAbstractNode::NodeAction act, const QVariant &v)
{
    DAAbstractNodeGraphicsItem *item = graphicsItem();

    if (item) {
        item->nodeAction((int)act, v);
    }
}

/**
 * @brief 输入参数
 * @param inputName
 * @param dp 数据会发生拷贝操作，不会影响原来数据包
 */
void DAAbstractNode::input(const QString &key, DADataPackage dp)
{
    d_ptr->_inputData[key] = dp.copy();
    callItemNodeHasAction(NodeInputed, key);
}

/**
 * @brief FCAbstractNode::inputData
 * @param key
 * @return
 */
DADataPackage DAAbstractNode::inputData(const QString &key)
{
    return (d_ptr->_inputData.value(key));
}
